home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / src / utilunix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  13.0 KB  |  536 lines

  1. /* Various utilities - Unix variants
  2.    Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
  3.    Written 1994, 1995, 1996 by:
  4.    Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
  5.    Jakub Jelinek, Mauricio Plaza.
  6.  
  7.    The file_date routine is mostly from GNU's fileutils package,
  8.    written by Richard Stallman and David MacKenzie.
  9.  
  10.    This program is free software; you can redistribute it and/or modify
  11.    it under the terms of the GNU General Public License as published by
  12.    the Free Software Foundation; either version 2 of the License, or
  13.    (at your option) any later version.
  14.    
  15.    This program is distributed in the hope that it will be useful,
  16.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.    GNU General Public License for more details.
  19.  
  20.    You should have received a copy of the GNU General Public License
  21.    along with this program; if not, write to the Free Software
  22.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  23.  
  24. #include <config.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <sys/types.h>
  28. #ifdef HAVE_UNISTD_H
  29. #include <unistd.h>
  30. #endif
  31. #include <fcntl.h>
  32. #include <signal.h>        /* my_system */
  33. #include <limits.h>        /* INT_MAX */
  34. #include <sys/time.h>        /* select: timeout */
  35. #include <sys/param.h>
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include <stdarg.h>
  39. #include <sys/wait.h>        /* my_system */
  40. #include <errno.h>        /* my_system */
  41. #include <time.h>
  42. #include <pwd.h>
  43. #include <grp.h>
  44. #include <string.h>
  45. #include <ctype.h>
  46. #ifdef IS_AIX
  47. #  include <sys/select.h>
  48. #endif
  49. #ifdef SCO_FLAVOR
  50. #   include <time.h>
  51. #   include <sys/timeb.h>
  52. #endif
  53. #ifdef __linux__
  54. #  include <linux/termios.h>    /* This is needed for TIOCLINUX */
  55. #  include <sys/ioctl.h>
  56. #endif
  57. #include "util.h"
  58. #include "global.h"
  59. #include "fsusage.h"
  60. #include "mountlist.h"
  61.  
  62. struct sigaction startup_handler;
  63.  
  64. #define UID_CACHE_SIZE 200
  65. #define GID_CACHE_SIZE 30
  66.  
  67. typedef struct {
  68.     int  index;
  69.     char *string;
  70. } int_cache;
  71.  
  72. int_cache uid_cache [UID_CACHE_SIZE];
  73. int_cache gid_cache [GID_CACHE_SIZE];
  74.  
  75. void init_uid_gid_cache (void)
  76. {
  77.     int i;
  78.  
  79.     for (i = 0; i < UID_CACHE_SIZE; i++)
  80.     uid_cache [i].string = 0;
  81.  
  82.     for (i = 0; i < GID_CACHE_SIZE; i++)
  83.      gid_cache [i].string = 0;
  84. }
  85.  
  86. static char *i_cache_match (int id, int_cache *cache, int size)
  87. {
  88.     int i;
  89.  
  90.     for (i = 0; i < size; i++)
  91.     if (cache [i].index == id)
  92.         return cache [i].string;
  93.     return 0;
  94. }
  95.  
  96. static void i_cache_add (int id, int_cache *cache, int size, char *text,
  97.              int *last)
  98. {
  99.     if (cache [*last].string)
  100.     free (cache [*last].string);
  101.     cache [*last].string = strdup (text);
  102.     cache [*last].index = id;
  103.     *last = ((*last)+1) % size;
  104. }
  105.  
  106. char *get_owner (int uid)
  107. {
  108.     struct passwd *pwd;
  109.     static char ibuf [8];
  110.     char   *name;
  111.     static uid_last;
  112.     
  113.     if ((name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE)) != NULL)
  114.     return name;
  115.     
  116.     pwd = getpwuid (uid);
  117.     if (pwd){
  118.     i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
  119.     return pwd->pw_name;
  120.     }
  121.     else {
  122.     sprintf (ibuf, "%d", uid);
  123.     return ibuf;
  124.     }
  125. }
  126.  
  127. char *get_group (int gid)
  128. {
  129.     struct group *grp;
  130.     static char gbuf [8];
  131.     char *name;
  132.     static int  gid_last;
  133.     
  134.     if ((name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE)) != NULL)
  135.     return name;
  136.     
  137.     grp = getgrgid (gid);
  138.     if (grp){
  139.     i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
  140.     return grp->gr_name;
  141.     } else {
  142.     sprintf (gbuf, "%d", gid);
  143.     return gbuf;
  144.     }
  145. }
  146.  
  147. int my_system (int as_shell_command, const char *shell, const char *command)
  148. {
  149.     struct sigaction ignore, save_intr, save_quit, save_stop;
  150.     pid_t pid;
  151.     int status = 0;
  152.  
  153.     ignore.sa_handler = SIG_IGN;
  154.     sigemptyset (&ignore.sa_mask);
  155.     ignore.sa_flags = 0;
  156.     
  157.     sigaction (SIGINT, &ignore, &save_intr);    
  158.     sigaction (SIGQUIT, &ignore, &save_quit);
  159.  
  160.     /* Restore the original SIGTSTP handler, we don't want ncurses' */
  161.     /* handler messing the screen after the SIGCONT */
  162.     sigaction (SIGTSTP, &startup_handler, &save_stop);
  163.  
  164.     if ((pid = fork ()) < 0){
  165.     fprintf (stderr, "\n\nfork () = -1\n");
  166.     return -1;
  167.     }
  168.     if (pid == 0){
  169.     sigaction (SIGINT,  &save_intr, NULL);
  170.     sigaction (SIGQUIT, &save_quit, NULL);
  171.  
  172. #if 0
  173.     prepare_environment ();
  174. #endif
  175.     
  176.     if (as_shell_command)
  177.         execl (shell, shell, "-c", command, (char *) 0);
  178.     else
  179.         execlp (shell, shell, command, (char *) 0);
  180.  
  181.     _exit (127);        /* Exec error */
  182.     } else {
  183.     while (waitpid (pid, &status, 0) < 0)
  184.         if (errno != EINTR){
  185.         status = -1;
  186.         break;
  187.         }
  188.     }
  189.     sigaction (SIGINT,  &save_intr, NULL);
  190.     sigaction (SIGQUIT, &save_quit, NULL);
  191.     sigaction (SIGTSTP, &save_stop, NULL);
  192.  
  193.     return status;
  194. }
  195.  
  196. /* Returns a newly allocated string, if directory does not exist, return 0 */
  197. char *tilde_expand (char *directory)
  198. {
  199.     struct passwd *passwd;
  200.     char *p;
  201.     char *name;
  202.     int  len;
  203.     
  204.     if (*directory != '~')
  205.     return strdup (directory);
  206.  
  207.     directory++;
  208.     
  209.     p = strchr (directory, PATH_SEP);
  210.     
  211.     /* d = "~" or d = "~/" */
  212.     if (!(*directory) || (*directory == PATH_SEP)){
  213.     passwd = getpwuid (geteuid ());
  214.     p = (*directory == PATH_SEP) ? directory+1 : "";
  215.     } else {
  216.     if (!p){
  217.         p = "";
  218.         passwd = getpwnam (directory);
  219.     } else {
  220.         name = xmalloc (p - directory + 1, "tilde_expand");
  221.         strncpy (name, directory, p - directory);
  222.         name [p - directory] = 0;
  223.         passwd = getpwnam (name);
  224.         free (name);
  225.     }
  226.     }
  227.  
  228.     /* If we can't figure the user name, return NULL */
  229.     if (!passwd)
  230.     return 0;
  231.  
  232.     len = strlen (passwd->pw_dir) + strlen (p) + 2;
  233.     directory = xmalloc (len, "tilde_expand");
  234.     strcpy (directory, passwd->pw_dir);
  235.     strcat (directory, PATH_SEP_STR);
  236.     strcat (directory, p);
  237.     return directory;
  238. }
  239.  
  240. /* Pipes are guaranteed to be able to hold at least 4096 bytes */
  241. /* More than that would be unportable */
  242. #define MAX_PIPE_SIZE 4096
  243.  
  244. static int error_pipe[2];    /* File descriptors of error pipe */
  245. static int old_error;        /* File descriptor of old standard error */
  246.  
  247. /* Creates a pipe to hold standard error for a later analysis. */
  248. /* The pipe can hold 4096 bytes. Make sure no more is written */
  249. /* or a deadlock might occur. */
  250. void open_error_pipe (void)
  251. {
  252.     if (pipe (error_pipe) < 0){
  253.     message (0, " Warning ", " Pipe failed ");
  254.     }
  255.     old_error = dup (2);
  256.     if(old_error < 0 || close(2) || dup (error_pipe[1]) != 2){
  257.     message (0, " Warning ", " Dup failed ");
  258.     close (error_pipe[0]);
  259.     close (error_pipe[1]);
  260.     }
  261.     close (error_pipe[1]);
  262. }
  263.  
  264. void close_error_pipe (int error, char *text)
  265. {
  266.     char *title;
  267.     char msg[MAX_PIPE_SIZE];
  268.     int len = 0;
  269.  
  270.     if (error)
  271.     title = " Error ";
  272.     else
  273.     title = " Warning ";
  274.     if (old_error >= 0){
  275.     close (2);
  276.     dup (old_error);
  277.     close (old_error);
  278.     len = read (error_pipe[0], msg, MAX_PIPE_SIZE);
  279.  
  280.     if (len >= 0)
  281.         msg[len] = 0;
  282.     close (error_pipe[0]);
  283.     }
  284.     if (error < 0)
  285.     return;        /* Just ignore error message */
  286.     if (text == NULL){
  287.     if (len == 0) return;    /* Nothing to show */
  288.  
  289.     /* Show message from pipe */
  290.     message (error, title, msg);
  291.     } else {
  292.     /* Show given text and possible message from pipe */
  293.     message (error, title, " %s \n %s ", text, msg);
  294.     }
  295. }
  296.  
  297. /* Checks for messages in the error pipe,
  298.  * closes the pipe and displays an error box if needed
  299.  */
  300. void check_error_pipe (void)
  301. {
  302.     char error[MAX_PIPE_SIZE];
  303.     int len = 0;
  304.     if (old_error >= 0){
  305.     while (len < MAX_PIPE_SIZE)
  306.     {
  307.             struct fd_set select_set;
  308.             struct timeval timeout;
  309.             FD_ZERO (&select_set);
  310.             FD_SET (error_pipe[0], &select_set);
  311.             timeout.tv_sec = 0;
  312.             timeout.tv_usec = 0;
  313.             select (FD_SETSIZE, &select_set, 0, 0, &timeout);
  314.             if (!FD_ISSET (0, &select_set))
  315.         break;
  316.         read (error_pipe[0], error + len, 1);
  317.         len ++;
  318.     }
  319.     error[len] = 0;
  320.     close (error_pipe[0]);
  321.     }
  322.     if (len > 0)
  323.         message (0, " Warning ", error);
  324. }
  325.  
  326. static struct sigaction ignore, save_intr, save_quit, save_stop;
  327.  
  328. /* INHANDLE is a result of some mc_open call to any vfs, this function
  329.    returns a normal handle (to be used with read) of a pipe for reading
  330.    of the output of COMMAND with arguments ... (must include argv[0] as
  331.    well) which gets as its input at most INLEN bytes from the INHANDLE
  332.    using mc_read. You have to call mc_doublepclose to close the returned
  333.    handle afterwards. If INLEN is -1, we read as much as we can :) */
  334. int mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
  335. {
  336.     int pipe0 [2], pipe1 [2];
  337.     pid_t pid;
  338.  
  339. #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
  340.  
  341.     pipe (pipe0); pipe (pipe1);
  342.     ignore.sa_handler = SIG_IGN;
  343.     sigemptyset (&ignore.sa_mask);
  344.     ignore.sa_flags = 0;
  345.         
  346.     sigaction (SIGINT, &ignore, &save_intr);
  347.     sigaction (SIGQUIT, &ignore, &save_quit);
  348.     sigaction (SIGTSTP, &startup_handler, &save_stop);
  349.  
  350.     switch (pid = fork ()) {
  351.         case -1:
  352.             closepipes ();
  353.         return -1;
  354.     case 0:
  355.         sigaction (SIGINT, &save_intr, NULL);
  356.         sigaction (SIGQUIT, &save_quit, NULL);
  357.  
  358.         switch (pid = fork ()) {
  359.             case -1:
  360.                 closepipes ();
  361.                 exit (1);
  362.             case 0:
  363.             {
  364. #define MAXARGS 16
  365.                 int argno;
  366.             char *args[MAXARGS];
  367.             va_list ap;
  368.             int nulldevice;
  369.  
  370.             nulldevice = open ("/dev/null", O_WRONLY);
  371.                 close (0);
  372.             dup (pipe0 [0]);
  373.             close (1);
  374.             dup (pipe1 [1]);
  375.             close (2);
  376.             dup (nulldevice);
  377.             close (nulldevice);
  378.             closepipes ();
  379.             va_start (ap, command);
  380.             argno = 0;
  381.             while ((args[argno++] = va_arg(ap, char *)) != NULL)
  382.               if (argno == (MAXARGS - 1)) {
  383.                   args[argno] = NULL;
  384.                   break;
  385.             }
  386.             va_end (ap);
  387.             execvp (command, args);
  388.             exit (0);
  389.               }
  390.             default:
  391.                 {
  392.                     char buffer [512];
  393.                     int i;
  394.  
  395.                     close (pipe0 [0]);
  396.                     close (pipe1 [0]);
  397.                     close (pipe1 [1]);
  398.                     while ((i = mc_read (inhandle, buffer,
  399.                                              (inlen == -1 || inlen > 512) 
  400.                                              ? 512 : inlen)) > 0) {
  401.                         write (pipe0 [1], buffer, i);
  402.                         if (inlen != -1) {
  403.                     inlen -= i;
  404.                     if (!inlen)
  405.                     break;
  406.                 }
  407.                     }
  408.                     close (pipe0 [1]);
  409.                while (waitpid (pid, &i, 0) < 0)
  410.                 if (errno != EINTR)
  411.                 break;
  412.                exit (i);
  413.                 }
  414.         }
  415.     default:
  416.         *the_pid = pid;
  417.         break;
  418.     }
  419.     close (pipe0 [0]);
  420.     close (pipe0 [1]);
  421.     close (pipe1 [1]);
  422.     return pipe1 [0];
  423. }
  424.  
  425. int mc_doublepclose (int pipe, pid_t pid)
  426. {
  427.     int status = 0;
  428.     
  429.     close (pipe);
  430.     waitpid (pid, &status, 0);
  431.     sigaction (SIGINT, &save_intr, NULL);
  432.     sigaction (SIGQUIT, &save_quit, NULL);
  433.     sigaction (SIGTSTP, &save_stop, NULL);
  434.  
  435.     return status;    
  436. }
  437.  
  438. /* Canonicalize path, and return a new path. Do everything in situ.
  439.    The new path differs from path in:
  440.     Multiple `/'s are collapsed to a single `/'.
  441.     Leading `./'s and trailing `/.'s are removed.
  442.     Trailing `/'s are removed.
  443.     Non-leading `../'s and trailing `..'s are handled by removing
  444.     portions of the path. */
  445. char *canonicalize_pathname (char *path)
  446. {
  447.     int i, start;
  448.     char stub_char;
  449.  
  450.     stub_char = (*path == PATH_SEP) ? PATH_SEP : '.';
  451.  
  452.     /* Walk along path looking for things to compact. */
  453.     i = 0;
  454.     for (;;) {
  455.         if (!path[i])
  456.         break;
  457.  
  458.           while (path[i] && path[i] != PATH_SEP)
  459.         i++;
  460.  
  461.           start = i++;
  462.  
  463.           /* If we didn't find any slashes, then there is nothing left to do. */
  464.           if (!path[start])
  465.         break;
  466.  
  467.         /* Handle multiple `/'s in a row. */
  468.         while (path[i] == PATH_SEP)
  469.         i++;
  470.  
  471.         if ((start + 1) != i) {
  472.         strcpy (path + start + 1, path + i);
  473.         i = start + 1;
  474.     }
  475.  
  476.         /* Handle backquoted `/'. */
  477.         if (start > 0 && path[start - 1] == '\\')
  478.         continue;
  479.  
  480.         /* Check for trailing `/'. */
  481.         if (start && !path[i]) {
  482.     zero_last:
  483.         path[--i] = '\0';
  484.         break;
  485.     }
  486.  
  487.         /* Check for `../', `./' or trailing `.' by itself. */
  488.         if (path[i] == '.') {
  489.         /* Handle trailing `.' by itself. */
  490.         if (!path[i + 1])
  491.             goto zero_last;
  492.  
  493.         /* Handle `./'. */
  494.         if (path[i + 1] == PATH_SEP) {
  495.             strcpy (path + i, path + i + 1);
  496.             i = start;
  497.             continue;
  498.         }
  499.  
  500.         /* Handle `../' or trailing `..' by itself. 
  501.            Remove the previous ?/ part with the exception of
  502.            ../, which we should leave intact. */
  503.         if (path[i + 1] == '.' && (path[i + 2] == PATH_SEP || !path[i + 2])) {
  504.             while (--start > -1 && path[start] != PATH_SEP);
  505.             if (!strncmp (path + start + 1, "../", 3))
  506.                 continue;
  507.             strcpy (path + start + 1, path + i + 2);
  508.             i = start;
  509.             continue;
  510.         }
  511.     }
  512.     }
  513.  
  514.     if (!*path) {
  515.         *path = stub_char;
  516.         path[1] = '\0';
  517.     }
  518.     return path;
  519. }
  520.  
  521. #ifdef SCO_FLAVOR
  522. int gettimeofday( struct timeval * tv, struct timezone * tz)
  523. {
  524.     struct timeb tb;
  525.     struct tm * l;
  526.     
  527.     ftime( &tb );
  528.     if (errno == EFAULT)
  529.     return -1;
  530.     l = localtime(&tb.time);
  531.     tv->tv_sec = l->tm_sec;
  532.     tv->tv_usec = (long) tb.millitm;
  533.     return 0;
  534. }
  535. #endif /* SCO_FLAVOR */
  536.